#include "mpctrl.h"
#include "mpnet.h"
#include "mpconfig.h"
#include "mphandle.h"

bool CMpCtrl::s_bReloadConfig = false;
bool CMpCtrl::s_bExit = false;

CMpCtrl::CMpCtrl()
{
    m_iNetPid = 0;
}

CMpCtrl::~CMpCtrl()
{
}

void CMpCtrl::Init()
{
    //安装信号处理函数
    signal(SIGUSR1, CMpCtrl::sigusr1_handle);	// kill -s USR1 pid 会让程序重读配置
    signal(SIGUSR2, CMpCtrl::sigusr2_handle);	// kill -s USR2 pid 会让程序安全的退出

    // 读取配置
    CONF->LoadConfig();

    // 创建网络子进程
    ForkNetProcess();
    CT_INFO(("fork net process ok"));

    sleep(1);
    CheckNetProcess(); // 如果网络子进程启动失败就退出

    // 创建工作子进程
    ForkWorkerProcess();
    CT_INFO(("fork worker process ok"));

    CT_INFO(("mpsvr init ok"));
}

void CMpCtrl::Run()
{
    while (true)
    {
        // 检查信号
        if (s_bReloadConfig)
        {
            CT_INFO(("recv signal, mpsvr reload config"));
            s_bReloadConfig = false;
            ReloadConfig();
            SignalProcess(SIGUSR1);
        }

        if (s_bExit)
        {
            CT_INFO(("recv signal, mpsvr exit"));
            s_bExit = false;
            SignalProcess(SIGUSR2);
            return;
        }

        CheckNetProcess(); // 如果网络子进程不存在就退出
        if (CheckWorkerProcess() == 0) //没有子进程退出
        {
            usleep(100000);
        }
    }
}

void CMpCtrl::Exit()
{
    CT_INFO(("mpsvr exit"));
}

void CMpCtrl::ReloadConfig()
{
}

void CMpCtrl::ForkNetProcess()
{
    pid_t iPid = fork();
    if (iPid == -1)
    {
        CT_THROW(("fork net process failed %d", CT_ERRNO));
    }
    else if (iPid > 0) // 父进程
    {
        m_iNetPid = iPid;
    }
    else // 子进程
    {
        try
        {
            CMpNet net;
            net.Init();
            net.Run();
            net.Exit();
        }
        catch (CTException& CT_UNUSED(e))
        {
        }

        exit(0);
    }
}

void CMpCtrl::CheckNetProcess()
{
    if (m_iNetPid <= 0)
    {
        CT_THROW(("net process not fork"));
    }

    // 检查是否有子进程退出
    int iRet = 0;
    int iStatus = 0;
    do
    {
        iRet = waitpid(m_iNetPid, &iStatus, WNOHANG|WUNTRACED);
    }
    while (iRet < 0 && CT_ERRNO == CT_EINTR);

    if (iRet == 0) // 没有子进程退出
    {
        return;
    }
    else if (iRet > 0) // 有子进程退出
    {
        int iTempPid = m_iNetPid;
        m_iNetPid = 0; // 置空
        CT_THROW(("waitpid(%d), find net process(%d) exited, status(%d)", iTempPid, iRet, iStatus));
    }
    else
    {
        CT_THROW(("not expected: file=%s line=%d ret=%d errno=%d", __FILE__, __LINE__, iRet, CT_ERRNO));
    }
}

void CMpCtrl::ForkWorkerProcess()
{
    m_vecWorker.reserve(CONF->ProcessNum);
    for (int i = 0; i < CONF->ProcessNum; ++i)
    {
        CProcessData data;
        m_vecWorker.push_back(data);
    }

    for (size_t i = 0; i < m_vecWorker.size(); ++i)
    {
        m_vecWorker[i].m_iChildPid = 0;
        try
        {
            ForkOneWorker(i);
        }
        catch (CTException& CT_UNUSED(e))
        {
            // 创建工作子进程失败, 可能是资源不够用
            // 这里sleep几秒后再继续, 没有创建的子进程在Run中还会再创建
            sleep(5);
        }
    }
}

int CMpCtrl::CheckWorkerProcess()
{
    int iRet = 0;
    int iStatus = 0;
    int iStopedProcessCount = 0;

    for (size_t i = 0; i < m_vecWorker.size(); ++i)
    {
        pid_t& iPid = m_vecWorker[i].m_iChildPid;
        if (iPid <= 0)
        {
            // 子进程不存在就创建进程
            try
            {
                ForkOneWorker(i);
            }
            catch (CTException& CT_UNUSED(e))
            {
                // 创建工作子进程失败, 可能是资源不够用
                // 这里sleep几秒后再继续, 没有创建的子进程在Run中还会再创建
                sleep(5);
            }

            continue;
        }

        // 检查是否有子进程退出
        do
        {
            iRet = waitpid(iPid, &iStatus, WNOHANG|WUNTRACED);
        }
        while(iRet < 0 && CT_ERRNO == CT_EINTR);

        if (iRet == 0) // 没有子进程退出
        {
            continue;
        }
        else if (iRet > 0 || CT_ERRNO == ECHILD) // 有子进程退出
        {
            CT_TRACE(("waitpid(%d), find worker process(%d) exited, status(%d)", iPid, iRet, iStatus));
            m_vecWorker[i].m_iChildPid = 0; // 置空
            iStopedProcessCount++;
        }
        else
        {
            CT_ERROR(("not expected: file=%s line=%d ret=%d errno=%d", __FILE__, __LINE__, iRet, CT_ERRNO));
        }
    }

    return iStopedProcessCount;
}

void CMpCtrl::ForkOneWorker(int iIndex)
{
    pid_t iPid = fork();
    if (iPid == -1)
    {
        CT_THROW(("fork worker process failed %d", CT_ERRNO));
    }
    else if (iPid > 0) // 父进程
    {
        m_vecWorker[iIndex].m_iChildPid = iPid;
        return;
    }
    else  // 子进程
    {
        try
        {
            CMpHandle handle;
            handle.Init();
            handle.Run();
            handle.Exit();
        }
        catch (CTException& CT_UNUSED(e))
        {
        }
        exit(0);
    }
}

void CMpCtrl::sigusr1_handle(int CT_UNUSED(iSigVal))
{
    s_bReloadConfig = true;
    signal(SIGUSR1, CMpCtrl::sigusr1_handle);
}

void CMpCtrl::sigusr2_handle(int CT_UNUSED(iSigVal))
{
    s_bExit = true;
    signal(SIGUSR2, CMpCtrl::sigusr1_handle);
}

void CMpCtrl::SignalProcess(int iSigVal)
{
    int iRet = 0;

    if (m_iNetPid <= 0)
    {
        CT_WARNING(("net process pid(%d) <= 0, can't signal(%d)", m_iNetPid, iSigVal));
    }
    else
    {
        iRet = kill(m_iNetPid, iSigVal);
        if (iRet)
        {
            CT_WARNING(("kill net process pid(%d) failed %d, errno=%d", m_iNetPid, iRet, CT_ERRNO));
        }
    }

    for (size_t i = 0; i < m_vecWorker.size(); ++i)
    {
        pid_t& iPid = m_vecWorker[i].m_iChildPid;
        if (iPid <= 0)
        {
            CT_WARNING(("worker process pid(%d) <= 0, can't signal(%d)", iPid, iSigVal));
        }
        else
        {
            iRet = kill(iPid, iSigVal);
            if (iRet)
            {
                CT_WARNING(("kill worker process pid(%d) failed %d, errno=%d", iPid, iRet, CT_ERRNO));
            }
        }
    }
}
